package de.haw.gka.a1;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Rectangle;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Iterator;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;

import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

import org.jgraph.graph.AttributeMap;
import org.jgraph.graph.DefaultGraphCell;
import org.jgraph.graph.GraphConstants;
import org.jgrapht.UndirectedGraph;
import org.jgrapht.alg.EulerianCircuit;
import org.jgrapht.ext.JGraphModelAdapter;
import org.jgrapht.graph.AbstractGraph;
import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.graph.DefaultWeightedEdge;
import org.jgrapht.graph.WeightedPseudograph;

public class SearchDia extends JDialog implements SearchTyps {
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private JGraphModelAdapter<?, ?> graphAdapter;
	
	public SearchDia(int aSearchTyp, AbstractGraph<?, ?> graph){
		searchTyp = aSearchTyp;
		if(graph ==  null){
			JOptionPane.showMessageDialog(this, "Es wurde noch kein Graph geladen", "Error...", JOptionPane.ERROR_MESSAGE);
		}else{
			tgraph = graph;
			init();
			this.setVisible(true);
		}	
	}

	private void init(){
		this.setLayout(null);
		//(x,y, with, heigth)
		lblAnfang.setBounds(new Rectangle(5,5,50,21));
		txtFldBegin.setBounds(new Rectangle(50,5,85,21));
		
		lblEnde.setBounds(new Rectangle(5,45,50,21));
		txtEnde.setBounds(new Rectangle(50,45,85,21));
		
		btnSearch.setBounds(new Rectangle(145,5,75,21));
		btnCancel.setBounds(new Rectangle(145,45,75,21));
		
		txtAreaResult.setEditable(false);
		resultSrcPn.getViewport().add(txtAreaResult, BorderLayout.CENTER);
		
		resultSrcPn.setBounds(230, 5, 235, 95);
		txtAreaResult.setForeground(Color.BLACK);
		this.add(lblAnfang,null);
		this.add(txtFldBegin,null);
		this.add(lblEnde,null);
		this.add(txtEnde,null);
		this.add(btnCancel,null);
		this.add(btnSearch,null);
		this.add(resultSrcPn,null);
		
		this.setSize(500,200);
		action();
		differents();
	}
	
	private void out(String str){
		System.out.println(str);
	}
	public void setMainFrame(JFrame aframe){
		mainFrame = aframe;
	}
	
	private JFrame mainFrame;
	
	private void action(){
		btnSearch.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				searchAction();				
			}
		});
		
		btnCancel.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				cancel();
			}
		});
	}
	
	
	void cancel(){
		this.setVisible(false);
	}
	
	void searchAction(){
        JGraphModelAdapter<Knoten, DefaultWeightedEdge> tempApater;
		String beging =  txtFldBegin.getText();
        long timeStart;
        int timeresult;
        JGraphModelAdapter theApater = new JGraphModelAdapter(tgraph);
        String end = txtEnde.getText();
		switch(searchTyp){
			case dfs:
				out("dfs");			
				AlgoDFS algoDfs = new AlgoDFS(tgraph, beging, end);
				setResult(""+algoDfs.getBenoetigteKanten(), ""+algoDfs.getTheShortestWay(), ""+algoDfs.getTheWay());
                setColor(algoDfs, tgraph, theApater);
                break;
			case bfs:
				out("bfs");
				AlgoBFS algoBFS = new AlgoBFS(tgraph, beging,end);
				setResult(""+algoBFS.getBenoetigteKanten(), ""+algoBFS.getTheShortestWay(), ""+algoBFS.getTheWay());
				setColor(algoBFS, tgraph, theApater);
                break;
			case djktra:
				out("djktra");
                AlgoDFS djktra = new AlgoDFS(tgraph, beging, end);
                setResult(""+djktra.getBenoetigteKanten(), ""+djktra.getTheShortestWay(), ""+djktra.getTheWay());
                setColor(djktra, tgraph, theApater);
                break;
			case astern:
				out("astern");
				Knoten start = getKnoten(tgraph, beging);
				Knoten ende = getKnoten(tgraph, end);
				WeightedPseudograph<Knoten, DefaultWeightedEdge> tmpGraph = (WeightedPseudograph<Knoten, DefaultWeightedEdge>) tgraph;
               	AStar astern= new AStar(tmpGraph, start, ende);
				setResult(""+astern.getPathLength(), ""+astern.getPathEdgeList(), "");	
				JGraphModelAdapter<Knoten, DefaultWeightedEdge> tmpApater = new JGraphModelAdapter<Knoten, DefaultWeightedEdge>(tmpGraph);
				setColor(astern, tmpGraph, tmpApater);
				break;
            case kruskal:
                out("Kruskal");
                AlgoKruskal algoKruskal = new AlgoKruskal(tgraph);
                tempApater = new JGraphModelAdapter<Knoten, DefaultWeightedEdge>(tgraph);
                setColor(algoKruskal, tgraph, tempApater);
                setResult(""+ algoKruskal.getGewicht(), "" + algoKruskal.getTheShortestWay().size(), "" +algoKruskal.getTheShortestWay());
                break;
            case prim:
                out("Prim");
                WeightedPseudograph tempGraph = (WeightedPseudograph)tgraph;
                AlgoPrim algoPrim = new AlgoPrim(tempGraph);
                tempApater = new JGraphModelAdapter<Knoten, DefaultWeightedEdge>(tempGraph);
                setResult(""+algoPrim.getGewicht(), "" + algoPrim.getTheShortestWay().size() , ""+ algoPrim.getTheShortestWay());
                setColor(algoPrim, tempGraph, tempApater);
                break;
            case primFibo:
                out("PrimFibo");
                timeStart = System.currentTimeMillis();
                AlgoPrimFibo algoPrimFibo = new AlgoPrimFibo(tgraph);
                timeresult = getZeitinMilliSec(timeStart);
                tempApater = new JGraphModelAdapter<Knoten, DefaultWeightedEdge>(tgraph);
                setResult(""+ algoPrimFibo.getGewicht() , ""+ algoPrimFibo.getTheShortestWay().size(), "" + algoPrimFibo.getTheShortestWay());
                txtAreaResult.setText(txtAreaResult.getText() + "\n"+ "Zeit: " + timeresult + " sec");
                setColor(algoPrimFibo, tgraph, tempApater);
                break;
            case hierholzer:
            	out("Hierholzer");
                //Graphen überprüfen ob Euler
                if(tgraph instanceof UndirectedGraph && EulerianCircuit.isEulerian((UndirectedGraph<Knoten, DefaultWeightedEdge>) tgraph)){
                    timeStart= System.currentTimeMillis();
                    AlgoHierholzer algoHierholzer = new AlgoHierholzer(tgraph);
                    timeresult = getZeitinMilliSec(timeStart);
                    tempApater = new JGraphModelAdapter<Knoten, DefaultWeightedEdge>(tgraph);
                    setResult(""+ algoHierholzer.getTheShortestWay().size() , ""+ algoHierholzer.getWay().size(), "" + algoHierholzer.getWay());
                    txtAreaResult.setText(txtAreaResult.getText() + "\n"+ "Zeit: " + timeresult + " ms");
                    setColor(algoHierholzer, tgraph, tempApater);
                }else
                    JOptionPane.showMessageDialog(mainFrame, "Dieser Graph ist unpassend für diesen Algo." +
                            "\nBedingung: Ungerichtet\n     Zusammenhändend\n       ist ein Eulerkreis" , "Error",
                            JOptionPane.ERROR_MESSAGE);
                break;
            case fleury:
            	out("Fleury");
                //Graphen überprüfen ob Euler
                if(tgraph instanceof UndirectedGraph && EulerianCircuit.isEulerian((UndirectedGraph<Knoten, DefaultWeightedEdge>) tgraph)){
                    timeStart= System.currentTimeMillis();
                    AlgoFleury algoFleury = new AlgoFleury(tgraph);
                    timeresult = getZeitinMilliSec(timeStart);
                    tempApater = new JGraphModelAdapter<Knoten, DefaultWeightedEdge>(tgraph);
                    setResult(""+ algoFleury.getTheShortestWay().size() , ""+ algoFleury.getWay().size(), "" + algoFleury.getWay());
                    txtAreaResult.setText(txtAreaResult.getText() + "\n"+ "Zeit: " + timeresult + " ms");
                    setColor(algoFleury, tgraph, tempApater);
                }else
                    JOptionPane.showMessageDialog(mainFrame, "Dieser Graph ist unpassend für diesen Algo." +
                            "\nBedingung: Ungerichtet\n     Zusammenhändend\n       ist ein Eulerkreis" , "Error",
                            JOptionPane.ERROR_MESSAGE);
                break;
			}
	}

    private int getZeitinMilliSec(long time){
        long nachher = System.currentTimeMillis();
        return (int) ((nachher - time));
    }

	private void setColor(AbstractSearchAlgo searchAlgo, AbstractGraph<?, ?> aGraph, JGraphModelAdapter<?,?>  adapter){
		
		Iterator<DefaultWeightedEdge> kantenSet = searchAlgo.getTheShortestWay().iterator();
		while(kantenSet.hasNext()){
            Object obj =    kantenSet.next();
            if(obj instanceof DefaultWeightedEdge){
                DefaultWeightedEdge dwE = (DefaultWeightedEdge)obj;
                graphAdapter = buildColor(adapter, dwE);
            }else if (obj instanceof DefaultEdge){
                DefaultEdge defaultEdge = (DefaultEdge) obj;
                graphAdapter = buildColor(adapter, defaultEdge);
            }

        }
        ((Main)mainFrame).updateGraph(tgraph, aGraph.vertexSet().size(), "New", graphAdapter);
	}


    private JGraphModelAdapter buildColor(JGraphModelAdapter<?,?> graphAdapter, DefaultWeightedEdge aObj){
        Object obj = null;
        graphAdapter.beginUpdate();
        for(int i = 0; i< graphAdapter.getRootCount(); i++){
            obj = graphAdapter.getRootAt(i);
          //  out("First: " + aObj +  " Sec: " + obj +" ---> " + aObj.toString().equals(obj.toString()));
            if(aObj.toString().equals(obj.toString())){
                setVertexColor(obj, Color.RED);
                continue;
            }
        }
        graphAdapter.endUpdate();
        return graphAdapter;
    }

    private JGraphModelAdapter buildColor(JGraphModelAdapter<?,?> graphAdapter, DefaultEdge aObj){
        Object obj = null;
        graphAdapter.beginUpdate();
        for(int i = 0; i< graphAdapter.getRootCount(); i++){
            obj = graphAdapter.getRootAt(i);

            if((aObj.toString()).equals(obj.toString())){
                setVertexColor(obj, Color.RED);
                continue;
            }
        }
        graphAdapter.endUpdate();
        return graphAdapter;
    }


	

	public void setVertexColor(Object vertex, Color color){
		DefaultGraphCell cell = (DefaultGraphCell) vertex;
        AttributeMap attr = cell.getAttributes();
        GraphConstants.setLineColor(attr, color);
    }
	

	private Knoten getKnoten(AbstractGraph<Knoten, DefaultWeightedEdge> aGraph, String knotenName){
		Iterator<Knoten> iterVertex =  aGraph.vertexSet().iterator();
		while(iterVertex.hasNext()){
			Knoten a = iterVertex.next();
			if(a.getName().equals(knotenName)) return a;
		}
		return null;
	}

	private void differents(){
		switch(searchTyp){
		case dfs:
			this.setTitle("Tiefensuche");
			break;
		case bfs:
			this.setTitle("Breitensuche");
			break;
		case astern:
			this.setTitle("A*");
			break;
        case djktra:
            this.setTitle("Djktra");
            break;
        case prim:
             this.setTitle("Prim");
               txtFldBegin.setEditable(false);
               txtEnde.setEditable(false);
             break;
        case primFibo:
            this.setTitle("PrimFibo");
            txtFldBegin.setEditable(false);
            txtEnde.setEditable(false);
		    break;
        case kruskal:
            this.setTitle("Kruskal");
            txtFldBegin.setEditable(false);
            txtEnde.setEditable(false);
            break;
        case hierholzer:
            this.setTitle("Hierholzer");
            txtFldBegin.setEditable(false);
            txtEnde.setEditable(false);
		    break;
        case fleury:
            this.setTitle("Fleury");
            txtFldBegin.setEditable(false);
            txtEnde.setEditable(false);
		    break;
        }
	}
	
	private void setResult(String knaten, String kWeg, String weg){
		txtAreaResult.setText("Benötige Kanten: " + knaten +"\n"
				+ "Kürzesterweg: " + kWeg + "\n"
				+ "Der Weg: " + weg);
	}
	
	private JLabel lblAnfang = new JLabel("Beginn");
	private JLabel lblEnde = new JLabel("Zu");
	
	private JTextField txtFldBegin = new JTextField();
	private JTextField txtEnde = new JTextField();
	
	private JTextArea txtAreaResult = new JTextArea();

	private JButton btnSearch = new JButton("Suche");
	private JButton btnCancel = new JButton("Cancel");
	private int searchTyp;
	
	private JScrollPane resultSrcPn = new JScrollPane();
	
	private AbstractGraph tgraph;

}

